import hashlib
import os
from datetime import datetime, timedelta,timezone
from typing import Literal, Union

import jwt
from fastapi.encoders import jsonable_encoder
from jwt import InvalidTokenError
from pydantic import BaseModel, Field
from fastapi import APIRouter, Form, File, UploadFile, HTTPException, Depends, Header,Path, Query
from fastapi import BackgroundTasks,Response,WebSocket
from fastapi.responses import HTMLResponse
from starlette import status
from fastapi.security import OAuth2PasswordRequestForm

from app.core.security import verify_password,get_password_hash
# 环境变量
from config import get_settings
from app.logging.config import log, ob1

# 环境变量
settings = get_settings()



router = APIRouter()
tags = ["教程示例"]
description = "fastapi 教程示例"


# 路径参数
@router.post("/pathPara/{p_id}/", summary="路径参数")
async def f01(p_id: int | None = Path(description="路径参数p_id", ge=1, le=100)):
    """
    - 路径参数不能有默认值
    - 路径参数注意与真实URL的冲突
    - 真实的URL请求应放在路径参数之前
    - 例：/path/me 应该在 /path/{str} 之前
    """
    return {"p_id": p_id, "message": "路径参数"}


@router.post("/queryPara/", summary="查询参数")
async def f02(
        pid: list[str] | None = Query(
            default=[1, 2, 3],
            alias="p_id",
            description="传入后端pid列表",
            min_length=3,
            max_length=6,

        )
):
    """
    **通用校验数据**
    - alias,description,title,deprecated

    **特定字符串校验**
    - min_length,max_length,regex
    """
    if pid:
        return {"p_id": pid, "message": "查询参数"}
    return {"p_id": None, "message": "查询参数"}


class F03Item(BaseModel):
    name: str
    description: str
    price: float
    tax: float | None = None


@router.post("/body/", summary="请求体")
async def f03(item: F03Item):
    """请求体
    Pydantic模型，在其PyCharm优化的功能如下：
    - 自动补全
    - 类型检查
    - 代码重构
    - 查找
    - 代码审查
    """
    return item


class F04Item(BaseModel):
    limit: int = Field(100, gt=0, le=100, description="分页数")
    offset: int = Field(0, ge=0)
    # order_by: Literal["desc", "asc"] = "asc"
    tags: list[str]


@router.post("/body2/", summary="查询参数模型")
async def f04(filter_query: F04Item = Query()):
    return filter_query


# 响应模型
class UserIn(BaseModel):
    username: str
    password: str
    email: str
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: str
    full_name: str | None = None


@router.post("/response1", summary="响应模型")
async def f05(user: UserIn) -> UserOut:
    return user


class UserInDB(BaseModel):
    username: str
    hashed_password: str
    email: str
    full_name: str | None = None
    yang: str | None = None


def fake_password_hasher(raw_password: str) -> str:
    return "supersecret" + raw_password


def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password, yang="yang9")
    print("User saved! ..not really")
    print(type(user_in_db), user_in_db)
    return user_in_db


@router.post("/api/v1/response2", summary="响应模型2", response_model=UserOut)
async def f06(user_in: UserIn):
    user_saved = fake_save_user(user_in)
    return user_saved


class BaseItem07(BaseModel):
    description: str
    type: str


class CarItem07(BaseItem07):
    type: str = "car"


class PlaneItem07(BaseItem07):
    type: str = "plane"
    size: int


items07 = {
    "item1": {"description": "All my friends drive a low rider", "type": "car"},
    "item2": {
        "description": "Music is my aeroplane, it's my aeroplane",
        "type": "plane",
        "size": 5,
    },
}


@router.get("/items/{item_id}/", summary="响应的Union", response_model=Union[PlaneItem07, CarItem07])
async def f07(item_id: str):
    return items07[item_id]


@router.post("/status/", summary="状态码", status_code=status.HTTP_200_OK)
async def f08():
    return {"status": "ok"}


class FormData(BaseModel):
    username: str
    password: str


@router.post("/form/", summary="表单数据")
async def f09(data: FormData = Form()):
    return data


async def save_files(file):
    # 文件名后缀时间戳
    suffix = datetime.now().strftime("%Y%m%d%H%M%S")
    path = 'static/files'
    # 不存在则创建，存在也不报错
    os.makedirs(path, exist_ok=True)
    file_name = file.filename.split('.')[0] + '_' + suffix + '.' + file.filename.split('.')[1]
    full_path = f"{path}/{file_name}"
    with open(full_path, 'wb') as f:
        while chunk := await file.read(1024 * 1024):  # 1M，避免大文件加载到内存
            f.write(chunk)


@router.post("/uploadfile/", summary="上传文件")
async def f10(files: list[UploadFile]):
    print(type(files), files)
    for file in files:
        await save_files(file)
    return {"filename": [file.filename for file in files]}


items11 = {"foo": "The Foo Wrestlers"}


@router.post("/httpexception/", summary="错误处理")
async def f11(item_id: str):
    if item_id not in items11:
        raise HTTPException(status_code=404, detail="Item not found", headers={"X-Error": "There goes my error"})
    return {item_id: items11[item_id]}


class Item12(BaseModel):
    title: str
    timestamp: str = datetime.now().strftime("%Y-%m-%d%H:%M:%S")
    description: str
    type: str


items12 = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@router.post('/json/', summary="json解码器")
async def f12(item: Item12):
    """
    1. 在没有日期类型的情况：
        - item.dict() === jsonable_encoder(item)
    2. 有日期的情况：
        - item.dict() 为日期形式
        - jsonable_encoder(item) 将日期转换为字符串
    """
    d1 = item.dict()
    json_compatible_item_data = jsonable_encoder(item)
    # items12['foo'] = {"name": "Foo", "price": 99.2, "tax": 111.1}
    # print(items12['foo'])
    # print('d1',d1)
    # print('json_compatible_item_data',json_compatible_item_data)
    return [d1, json_compatible_item_data]


async def common_parameters(
        q: str | None = None, skip: int = 0, limit: int = 100
):
    return {"q": q, "skip": skip, "limit": limit}


@router.post("/depends1/", summary="简单依赖项")
async def f13(commons: dict = Depends(common_parameters)):
    return commons


class CommonQueryParams:
    def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@router.post("/depends2/", summary="类依赖项")
async def f14(commons: CommonQueryParams = Depends(CommonQueryParams)):  # 简写commons:CommonQueryParams = Depends()
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    response.update({"items": commons})
    return response


async def verify_token(x_token: str = Header()):
    if x_token != 'fake-super-secret-token':
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def verify_key(x_key: str = Header()):
    if x_key != 'fake-super-secret-key':
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


@router.post("/depends3/", summary="路径操作装饰器依赖项")
async def f15():
    return [{"item": "Foo"}, {"item": "Bar"}]


from app.dependencies.t1 import oauth2_scheme


@router.post("/security1/", summary="安全1", tags=["安全"])
async def f16(token: str = Depends(oauth2_scheme)):
    return {"token": token}


class User17(BaseModel):
    username: str
    email: str | None = None
    full_name: str | None = None
    disabled: bool | None = None

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: str | None = None

fake_users_db = {
    "zhangsan": {
        "username": "zhangsan",
        "full_name": "张三哥",
        "email": "zhangsan@example.com",
        "hashed_password": "$2a$10$3CsWeZuVFsAUcIAk1rDhgOCLrnK0I.R/XtE.DshF2GJew/ANFif82",
        "disabled": False,
    },
    "lisi": {
        "username": "lisi",
        "full_name": "李四哥",
        "email": "lisi@example.com",
        "hashed_password": "$2a$10$3CsWeZuVFsAUcIAk1rDhgOCLrnK0I.R/XtE.DshF2GJew/ANFif82",
        "disabled": False,
    },
    "wangwu": {
        "username": "wangwu",
        "full_name": "王五哥",
        "email": "wangwu@example.com",
        "hashed_password": "$2a$10$KLQ/shYWzRjYc0eO7jH25OWi/lTa1Vt1unUMzamyEOIx8Qzcy8x4G",
        "disabled": True,
    },
}

SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30


def fake_hash_password(password):
    return password + '666'


class UserInDB17(User17):
    hashed_password: str


def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB17(**user_dict)

# 验证用户名及密码
def authenticate_user(fake_db, username: str, password: str):
    user = get_user(fake_db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

# 生成token
def create_access_token(data: dict, expires_delta: timedelta = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=15)

    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY,algorithm=ALGORITHM)
    return encoded_jwt

@router.post("/token/")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(fake_users_db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return Token(access_token=access_token, token_type="bearer")

# 获取用户
async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except InvalidTokenError:
        raise credentials_exception
    user = get_user(fake_users_db, username = token_data.username)
    if user is None:
        raise credentials_exception
    return user

# 获取活动的用户
async def get_current_active_user(current_user: User17 = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(status_code=403, detail="This user is disabled")
    return current_user

@router.post("/users/me", summary="当前用户", tags=["安全"])
async def f17(current_user: User17 = Depends(get_current_active_user)):
    return current_user


# def write_notification(email:str, message:str = ""):
#     with open("notifications.txt", "w") as f:
#         content = f"notification for {email}: {message}"
#         f.write(content)
#
# @router.post("/send-notification/{email}", summary="后台任务", tags=["后台任务"])
# async def send_notification(email: str, background_tasks: BackgroundTasks):
#     background_tasks.add_task(write_notification, email=email, message="some message")
#     return {"email": email,"message": "Notification sent in the background"}


@router.post("/xml", summary="返回xml")
async def send_xml():
    data = """<?xml version="1.0"?>
        <shampoo>
        <Header>
            Apply shampoo here.
        </Header>
        <Body>
            You'll have to use soap here.
        </Body>
        </shampoo>
        """
    return Response(content=data, media_type="application/xml")

@router.post("/html", summary="返回html", response_class=HTMLResponse)
async def send_html():
    html_content =  """
        <html>
            <head>
                <title>Some HTML in here</title>
            </head>
            <body>
                <h1>Look ma! HTML!</h1>
            </body>
        </html>
        """
    return HTMLResponse(content=html_content, media_type="text/html")

@router.post("/cookie-and-object", summary="响应Cookies", tags=["响应"])
async def send_cookies(response: Response):
    response.set_cookie(key="fakesession", value="999")
    return {"message": "Cookies sent"}


@router.post("/headers-and-object", summary="响应头", tags=["响应"])
async def send_headers_and_object(response: Response):
    response.headers["X-Yang"] = 'yangzhide'
    return {"message": "Hello World"}


class FixedContentQueryChecker:
    def __init__(self, fixed_content:str):
        self.fixed_content = fixed_content

    def __call__(self, q:str = ""):
        if q:
            return self.fixed_content in q
        return False

checher = FixedContentQueryChecker("He")
@router.post('/query-checker/', summary="依赖", tags=["高级依赖"])
async def query_checker(fixed_content_included:bool = Depends(checher)):
    return {"fixed_content_included": fixed_content_included}


html = """
<!DOCTYPE html>
<html>
    <head>
        <title>Chat</title>
    </head>
    <body>
        <h1>WebSocket Chat</h1>
        <form action="" onsubmit="sendMessage(event)">
            <input type="text" id="messageText" autocomplete="off"/>
            <button>Send</button>
        </form>
        <ul id='messages'>
        </ul>
        <script>
            var ws = new WebSocket("ws://localhost:9001/api/v1/api/v1/ws");
            ws.onmessage = function(event) {
                var messages = document.getElementById('messages')
                var message = document.createElement('li')
                var content = document.createTextNode(event.data)
                message.appendChild(content)
                messages.appendChild(message)
            };
            function sendMessage(event) {
                var input = document.getElementById("messageText")
                ws.send(input.value)
                input.value = ''
                event.preventDefault()
            }
        </script>
    </body>
</html>
"""

@router.get("/ws-send", summary="websocket", tags=["websocket"])
async def ws_send():
    return HTMLResponse(html)

@router.websocket("/ws")
async def ws(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Message text was: {data}")

@router.get("/predict", summary="生命周期1", tags=["生命周期"])
async def predict(x: float):
    log.error('测试错误')
    log.info('日志信息')
    log.critical('严重错误')
    ob1.error('测试错误')
    ob1.info('日志信息')
    ob1.critical('严重错误')
    return {"result": "6666666"}

@router.get('/getenv',summary="获取环境变量", tags=["获取环境变量"])
async def get_env():
    return {
        "summary": settings.summary
    }


class Subscription(BaseModel):
    username: str
    monthly_fee: float
    start_date: datetime


@router.post("new-subscription")
def new_subscription(body: Subscription):
    """
    When a new user subscribes to your service we'll send you a POST request with this
    data to the URL that you register for the event `new-subscription` in the dashboard.
    """


@router.get("/users999/")
def read_users():
    return ["Rick", "Morty"]